package com.example.codediff.service;

import com.example.codediff.analyzer.CallChainAnalyzer;
import com.example.codediff.analyzer.MethodDiffAnalyzer;
import com.example.codediff.model.CallChainResult;
import com.example.codediff.model.CallNode;
import com.example.codediff.model.CallPath;
import com.example.codediff.model.CompareResult;
import com.example.codediff.service.CompareResultCache;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * JAR调用链分析服务
 * 整合JAR比较和调用链分析功能
 */
@Slf4j
@Service
public class JarCallChainService {

    @Autowired
    private CallChainAnalyzer analyzer;

    @Autowired
    private CompareResultCache cache;

    @Autowired
    private MethodDiffAnalyzer methodDiffAnalyzer;

    public CallChainResult analyze(MultipartFile oldJarFile, MultipartFile newJarFile, String changedMethod) {
        Path oldJarPath = null;
        Path newJarPath = null;
        
        try {
            // 创建临时文件
            oldJarPath = createTempFile(oldJarFile);
            newJarPath = createTempFile(newJarFile);
            
            log.info("开始分析调用链: oldJar={}, newJar={}, changedMethod={}", 
                    oldJarPath, newJarPath, changedMethod);
            
            // 获取所有类
            Set<String> allClasses = listClasses(oldJarPath.toString(), newJarPath.toString(), "");
            log.info("找到的类总数: {}", allClasses.size());
            
            // 直接分析指定方法的调用链
            CallChainResult result = new CallChainResult();
            log.debug("分析变更点的调用链: {}", changedMethod);
            
            // 从变更点向上查找调用链
            Set<List<String>> chains = findCallChainsToEntryPoints(changedMethod, allClasses, oldJarPath.toString());
            
            if (!chains.isEmpty()) {
                // 转换为CallPath对象
                List<CallPath> callPaths = chains.stream()
                        .map(chain -> {
                            CallPath callPath = new CallPath();
                            // 为调用链中的每个方法创建CallNode
                            for (String methodSignature : chain) {
                                CallNode node = new CallNode();
                                // 解析方法签名
                                int lastDot = methodSignature.lastIndexOf('.');
                                if (lastDot > 0) {
                                    node.setClassName(methodSignature.substring(0, lastDot));
                                    node.setMethodName(methodSignature.substring(lastDot + 1));
                                } else {
                                    // 处理没有类名的情况
                                    node.setClassName("");
                                    node.setMethodName(methodSignature);
                                }
                                callPath.addNode(node);
                            }
                            return callPath;
                        })
                        .collect(Collectors.toList());
                
                result.addCallChains(changedMethod, callPaths);
            }
            
            log.info("调用链分析完成，共找到 {} 个调用链", result.getTotalCallChains());
            return result;
            
        } catch (Exception e) {
            log.error("分析调用链时发生错误", e);
            throw new RuntimeException("分析调用链失败: " + e.getMessage());
        } finally {
            // 清理临时文件
            deleteTempFile(oldJarPath);
            deleteTempFile(newJarPath);
        }
    }
    
    private Set<String> listClasses(String oldJarPath, String newJarPath, String packagePrefix) throws IOException {
        Set<String> classes = new HashSet<>();
        
        // 从两个JAR文件中收集类
        try (JarFile oldJar = new JarFile(new File(oldJarPath));
             JarFile newJar = new JarFile(new File(newJarPath))) {
            
            // 处理旧JAR
            collectClasses(oldJar, packagePrefix, classes);
            // 处理新JAR
            collectClasses(newJar, packagePrefix, classes);
        }
        
        return classes;
    }
    
    private void collectClasses(JarFile jar, String packagePrefix, Set<String> classes) {
        String prefix = packagePrefix.replace('.', '/');
        jar.stream()
           .map(JarEntry::getName)
           .filter(name -> name.endsWith(".class"))
           .map(name -> {
               if (name.startsWith("BOOT-INF/classes/")) {
                   return name.substring("BOOT-INF/classes/".length());
               }
               return name;
           })
           .filter(name -> name.startsWith(prefix))
           .map(name -> name.substring(0, name.length() - 6).replace('/', '.'))
           .forEach(classes::add);
    }
    
    private Path createTempFile(MultipartFile file) throws IOException {
        Path tempFile = Files.createTempFile("temp_", ".jar");
        try (InputStream in = file.getInputStream()) {
            Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING);
        }
        return tempFile;
    }
    
    private void deleteTempFile(Path path) {
        try {
            Files.deleteIfExists(path);
        } catch (IOException e) {
            log.warn("删除临时文件失败: {}", path, e);
        }
    }
    
    private Set<List<String>> findCallChainsToEntryPoints(String changedMethod, Set<String> allClasses, String jarPath) throws IOException {
        Set<List<String>> chains = new HashSet<>();
        
        // 解析变更点的类名和方法名
        int lastDot = changedMethod.lastIndexOf('.');
        if (lastDot <= 0) {
            log.warn("无效的方法名格式: {}", changedMethod);
            return chains;
        }
        
        String targetClass = changedMethod.substring(0, lastDot);
        String targetMethod = changedMethod.substring(lastDot + 1);
        
        // 从所有类中查找调用该方法的地方
        for (String className : allClasses) {
            ClassNode classNode = methodDiffAnalyzer.readClass(jarPath, className);
            if (classNode == null) continue;
            
            // 检查每个方法中的调用
            for (MethodNode method : classNode.methods) {
                if (method.instructions == null) continue;
                
                // 检查方法中的所有调用指令
                for (AbstractInsnNode insn = methodDiffAnalyzer.getFirstInstruction(method); 
                     insn != null; 
                     insn = methodDiffAnalyzer.getNextInstruction(insn)) {
                    if (insn instanceof MethodInsnNode) {
                        MethodInsnNode methodInsn = (MethodInsnNode) insn;
                        if (methodInsn.owner.equals(targetClass.replace('.', '/')) && 
                            methodInsn.name.equals(targetMethod)) {
                            // 找到一个调用点，记录调用链
                            List<String> chain = new ArrayList<>();
                            chain.add(className + "." + method.name);
                            chain.add(changedMethod);
                            chains.add(chain);
                        }
                    }
                }
            }
        }
        
        return chains;
    }
}
